-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[lldb][Expression] Add structor variant to LLDB's function call labels #149827
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[lldb][Expression] Add structor variant to LLDB's function call labels #149827
Conversation
aeebf1f
to
ce7fa42
Compare
ce7fa42
to
2a70642
Compare
✅ With the latest revision this PR passed the C/C++ code formatter. |
2a70642
to
44410a4
Compare
…#148877) LLDB currently attaches `AsmLabel`s to `FunctionDecl`s such that that the `IRExecutionUnit` can determine which mangled name to call (we can't rely on Clang deriving the correct mangled name to call because the debug-info AST doesn't contain all the info that would be encoded in the DWARF linkage names). However, we don't attach `AsmLabel`s for structors because they have multiple variants and thus it's not clear which mangled name to use. In the [RFC on fixing expression evaluation of abi-tagged structors](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816) we discussed encoding the structor variant into the `AsmLabel`s. Specifically in [this thread](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816/7) we discussed that the contents of the `AsmLabel` are completely under LLDB's control and we could make use of it to uniquely identify a function by encoding the exact module and DIE that the function is associated with (mangled names need not be enough since two identical mangled symbols may live in different modules). So if we already have a custom `AsmLabel` format, we can encode the structor variant in a follow-up (the current idea is to append the structor variant as a suffix to our custom `AsmLabel` when Clang emits the mangled name into the JITted IR). Then we would just have to teach the `IRExecutionUnit` to pick the correct structor variant DIE during symbol resolution. The draft of this is available [here](#149827) This patch sets up the infrastructure for the custom `AsmLabel` format by encoding the module id, DIE id and mangled name in it. **Implementation** The flow is as follows: 1. Create the label in `DWARFASTParserClang`. The format is: `$__lldb_func:module_id:die_id:mangled_name` 2. When resolving external symbols in `IRExecutionUnit`, we parse this label and then do a lookup by DIE ID (or mangled name into the module if the encoded DIE is a declaration). Depends on #151355
…n AsmLabels (#148877) LLDB currently attaches `AsmLabel`s to `FunctionDecl`s such that that the `IRExecutionUnit` can determine which mangled name to call (we can't rely on Clang deriving the correct mangled name to call because the debug-info AST doesn't contain all the info that would be encoded in the DWARF linkage names). However, we don't attach `AsmLabel`s for structors because they have multiple variants and thus it's not clear which mangled name to use. In the [RFC on fixing expression evaluation of abi-tagged structors](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816) we discussed encoding the structor variant into the `AsmLabel`s. Specifically in [this thread](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816/7) we discussed that the contents of the `AsmLabel` are completely under LLDB's control and we could make use of it to uniquely identify a function by encoding the exact module and DIE that the function is associated with (mangled names need not be enough since two identical mangled symbols may live in different modules). So if we already have a custom `AsmLabel` format, we can encode the structor variant in a follow-up (the current idea is to append the structor variant as a suffix to our custom `AsmLabel` when Clang emits the mangled name into the JITted IR). Then we would just have to teach the `IRExecutionUnit` to pick the correct structor variant DIE during symbol resolution. The draft of this is available [here](llvm/llvm-project#149827) This patch sets up the infrastructure for the custom `AsmLabel` format by encoding the module id, DIE id and mangled name in it. **Implementation** The flow is as follows: 1. Create the label in `DWARFASTParserClang`. The format is: `$__lldb_func:module_id:die_id:mangled_name` 2. When resolving external symbols in `IRExecutionUnit`, we parse this label and then do a lookup by DIE ID (or mangled name into the module if the encoded DIE is a declaration). Depends on llvm/llvm-project#151355
499aa52
to
9ec9eef
Compare
e10a1eb
to
82075a2
Compare
@llvm/pr-subscribers-debuginfo @llvm/pr-subscribers-lldb Author: Michael Buch (Michael137) ChangesDepends on #148877 Patch is 30.28 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/149827.diff 16 Files Affected:
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index b3ff45b3e90a3..a0efb21218312 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1059,22 +1059,13 @@ def AVRSignal : InheritableAttr, TargetSpecificAttr<TargetAVR> {
def AsmLabel : InheritableAttr {
let Spellings = [CustomKeyword<"asm">, CustomKeyword<"__asm__">];
let Args = [
- // Label specifies the mangled name for the decl.
- StringArgument<"Label">,
-
- // IsLiteralLabel specifies whether the label is literal (i.e. suppresses
- // the global C symbol prefix) or not. If not, the mangle-suppression prefix
- // ('\01') is omitted from the decl name at the LLVM IR level.
- //
- // Non-literal labels are used by some external AST sources like LLDB.
- BoolArgument<"IsLiteralLabel", /*optional=*/0, /*fake=*/1>
- ];
+ // Label specifies the mangled name for the decl.
+ StringArgument<"Label">, ];
let SemaHandler = 0;
let Documentation = [AsmLabelDocs];
- let AdditionalMembers =
-[{
+ let AdditionalMembers = [{
bool isEquivalent(AsmLabelAttr *Other) const {
- return getLabel() == Other->getLabel() && getIsLiteralLabel() == Other->getIsLiteralLabel();
+ return getLabel() == Other->getLabel();
}
}];
}
diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp
index 9652fdbc4e125..58029476572c5 100644
--- a/clang/lib/AST/Mangle.cpp
+++ b/clang/lib/AST/Mangle.cpp
@@ -152,6 +152,20 @@ bool MangleContext::shouldMangleDeclName(const NamedDecl *D) {
return shouldMangleCXXName(D);
}
+static llvm::StringRef g_lldb_func_call_label_prefix = "$__lldb_func";
+
+static void emitLLDBAsmLabel(llvm::StringRef label, GlobalDecl GD,
+ llvm::raw_ostream &Out) {
+ Out << g_lldb_func_call_label_prefix << ":";
+
+ if (llvm::isa<clang::CXXConstructorDecl>(GD.getDecl()))
+ Out << "C" << GD.getCtorType();
+ else if (llvm::isa<clang::CXXDestructorDecl>(GD.getDecl()))
+ Out << "D" << GD.getDtorType();
+
+ Out << label.substr(g_lldb_func_call_label_prefix.size() + 1);
+}
+
void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
const ASTContext &ASTContext = getASTContext();
const NamedDecl *D = cast<NamedDecl>(GD.getDecl());
@@ -161,9 +175,9 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
if (const AsmLabelAttr *ALA = D->getAttr<AsmLabelAttr>()) {
// If we have an asm name, then we use it as the mangling.
- // If the label isn't literal, or if this is an alias for an LLVM intrinsic,
+ // If the label is an alias for an LLVM intrinsic,
// do not add a "\01" prefix.
- if (!ALA->getIsLiteralLabel() || ALA->getLabel().starts_with("llvm.")) {
+ if (ALA->getLabel().starts_with("llvm.")) {
Out << ALA->getLabel();
return;
}
@@ -185,7 +199,11 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
if (!UserLabelPrefix.empty())
Out << '\01'; // LLVM IR Marker for __asm("foo")
- Out << ALA->getLabel();
+ if (ALA->getLabel().starts_with(g_lldb_func_call_label_prefix))
+ emitLLDBAsmLabel(ALA->getLabel(), GD, Out);
+ else
+ Out << ALA->getLabel();
+
return;
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index e2ac648320c0f..885d04b9ab926 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8113,9 +8113,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
}
}
- NewVD->addAttr(AsmLabelAttr::Create(Context, Label,
- /*IsLiteralLabel=*/true,
- SE->getStrTokenLoc(0)));
+ NewVD->addAttr(AsmLabelAttr::Create(Context, Label, SE->getStrTokenLoc(0)));
} else if (!ExtnameUndeclaredIdentifiers.empty()) {
llvm::DenseMap<IdentifierInfo*,AsmLabelAttr*>::iterator I =
ExtnameUndeclaredIdentifiers.find(NewVD->getIdentifier());
@@ -10345,9 +10343,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
if (Expr *E = D.getAsmLabel()) {
// The parser guarantees this is a string.
StringLiteral *SE = cast<StringLiteral>(E);
- NewFD->addAttr(AsmLabelAttr::Create(Context, SE->getString(),
- /*IsLiteralLabel=*/true,
- SE->getStrTokenLoc(0)));
+ NewFD->addAttr(
+ AsmLabelAttr::Create(Context, SE->getString(), SE->getStrTokenLoc(0)));
} else if (!ExtnameUndeclaredIdentifiers.empty()) {
llvm::DenseMap<IdentifierInfo*,AsmLabelAttr*>::iterator I =
ExtnameUndeclaredIdentifiers.find(NewFD->getIdentifier());
@@ -20598,8 +20595,8 @@ void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name,
LookupOrdinaryName);
AttributeCommonInfo Info(AliasName, SourceRange(AliasNameLoc),
AttributeCommonInfo::Form::Pragma());
- AsmLabelAttr *Attr = AsmLabelAttr::CreateImplicit(
- Context, AliasName->getName(), /*IsLiteralLabel=*/true, Info);
+ AsmLabelAttr *Attr =
+ AsmLabelAttr::CreateImplicit(Context, AliasName->getName(), Info);
// If a declaration that:
// 1) declares a function or a variable
diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp
index ed635da683aab..afaf413493299 100644
--- a/clang/unittests/AST/DeclTest.cpp
+++ b/clang/unittests/AST/DeclTest.cpp
@@ -74,7 +74,6 @@ TEST(Decl, AsmLabelAttr) {
StringRef Code = R"(
struct S {
void f() {}
- void g() {}
};
)";
auto AST =
@@ -87,11 +86,8 @@ TEST(Decl, AsmLabelAttr) {
const auto *DeclS =
selectFirst<CXXRecordDecl>("d", match(cxxRecordDecl().bind("d"), Ctx));
NamedDecl *DeclF = *DeclS->method_begin();
- NamedDecl *DeclG = *(++DeclS->method_begin());
- // Attach asm labels to the decls: one literal, and one not.
- DeclF->addAttr(AsmLabelAttr::Create(Ctx, "foo", /*LiteralLabel=*/true));
- DeclG->addAttr(AsmLabelAttr::Create(Ctx, "goo", /*LiteralLabel=*/false));
+ DeclF->addAttr(AsmLabelAttr::Create(Ctx, "foo"));
// Mangle the decl names.
std::string MangleF, MangleG;
@@ -99,14 +95,11 @@ TEST(Decl, AsmLabelAttr) {
ItaniumMangleContext::create(Ctx, Diags));
{
llvm::raw_string_ostream OS_F(MangleF);
- llvm::raw_string_ostream OS_G(MangleG);
MC->mangleName(DeclF, OS_F);
- MC->mangleName(DeclG, OS_G);
}
ASSERT_EQ(MangleF, "\x01"
"foo");
- ASSERT_EQ(MangleG, "goo");
}
TEST(Decl, MangleDependentSizedArray) {
diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h
index 6f27da7b9cadf..7b3983bc89367 100644
--- a/libcxxabi/src/demangle/ItaniumDemangle.h
+++ b/libcxxabi/src/demangle/ItaniumDemangle.h
@@ -1766,6 +1766,8 @@ class CtorDtorName final : public Node {
template<typename Fn> void match(Fn F) const { F(Basename, IsDtor, Variant); }
+ int getVariant() const { return Variant; }
+
void printLeft(OutputBuffer &OB) const override {
if (IsDtor)
OB += "~";
diff --git a/lldb/include/lldb/Expression/Expression.h b/lldb/include/lldb/Expression/Expression.h
index 20067f469895b..847226167d584 100644
--- a/lldb/include/lldb/Expression/Expression.h
+++ b/lldb/include/lldb/Expression/Expression.h
@@ -103,11 +103,15 @@ class Expression {
///
/// The format being:
///
-/// <prefix>:<module uid>:<symbol uid>:<name>
+/// <prefix>:<discriminator>:<module uid>:<symbol uid>:<name>
///
/// The label string needs to stay valid for the entire lifetime
/// of this object.
struct FunctionCallLabel {
+ /// Arbitrary string which language plugins can interpret for their
+ /// own needs.
+ llvm::StringRef discriminator;
+
/// Unique identifier of the lldb_private::Module
/// which contains the symbol identified by \c symbol_id.
lldb::user_id_t module_id;
@@ -133,7 +137,7 @@ struct FunctionCallLabel {
///
/// The representation roundtrips through \c fromString:
/// \code{.cpp}
- /// llvm::StringRef encoded = "$__lldb_func:0x0:0x0:_Z3foov";
+ /// llvm::StringRef encoded = "$__lldb_func:blah:0x0:0x0:_Z3foov";
/// FunctionCallLabel label = *fromString(label);
///
/// assert (label.toString() == encoded);
diff --git a/lldb/source/Expression/Expression.cpp b/lldb/source/Expression/Expression.cpp
index 796851ff15ca3..16ecb1d7deef8 100644
--- a/lldb/source/Expression/Expression.cpp
+++ b/lldb/source/Expression/Expression.cpp
@@ -34,10 +34,10 @@ Expression::Expression(ExecutionContextScope &exe_scope)
llvm::Expected<FunctionCallLabel>
lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
- llvm::SmallVector<llvm::StringRef, 4> components;
- label.split(components, ":", /*MaxSplit=*/3);
+ llvm::SmallVector<llvm::StringRef, 5> components;
+ label.split(components, ":", /*MaxSplit=*/4);
- if (components.size() != 4)
+ if (components.size() != 5)
return llvm::createStringError("malformed function call label.");
if (components[0] != FunctionCallLabelPrefix)
@@ -45,8 +45,10 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
"expected function call label prefix '{0}' but found '{1}' instead.",
FunctionCallLabelPrefix, components[0]));
- llvm::StringRef module_label = components[1];
- llvm::StringRef die_label = components[2];
+ llvm::StringRef discriminator = components[1];
+ llvm::StringRef module_label = components[2];
+ llvm::StringRef die_label = components[3];
+ llvm::StringRef lookup_name = components[4];
lldb::user_id_t module_id = 0;
if (!llvm::to_integer(module_label, module_id))
@@ -58,20 +60,23 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
return llvm::createStringError(
llvm::formatv("failed to parse symbol ID from '{0}'.", die_label));
- return FunctionCallLabel{/*.module_id=*/module_id,
+ return FunctionCallLabel{/*.discriminator=*/discriminator,
+ /*.module_id=*/module_id,
/*.symbol_id=*/die_id,
- /*.lookup_name=*/components[3]};
+ /*.lookup_name=*/lookup_name};
}
std::string lldb_private::FunctionCallLabel::toString() const {
- return llvm::formatv("{0}:{1:x}:{2:x}:{3}", FunctionCallLabelPrefix,
- module_id, symbol_id, lookup_name)
+ return llvm::formatv("{0}:{1}:{2:x}:{3:x}:{4}", FunctionCallLabelPrefix,
+ discriminator, module_id, symbol_id, lookup_name)
.str();
}
void llvm::format_provider<FunctionCallLabel>::format(
const FunctionCallLabel &label, raw_ostream &OS, StringRef Style) {
- OS << llvm::formatv("FunctionCallLabel{ module_id: {0:x}, symbol_id: {1:x}, "
- "lookup_name: {2} }",
- label.module_id, label.symbol_id, label.lookup_name);
+ OS << llvm::formatv("FunctionCallLabel{ discriminator: {0}, module_id: "
+ "{1:x}, symbol_id: {2:x}, "
+ "lookup_name: {3} }",
+ label.discriminator, label.module_id, label.symbol_id,
+ label.lookup_name);
}
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index 781c1c6c5745d..5c674308160e5 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -250,11 +250,52 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram,
return cv_quals;
}
+static const char *GetMangledOrStructorName(const DWARFDIE &die) {
+ const char *name = die.GetMangledName(/*substitute_name_allowed*/ false);
+ if (name)
+ return name;
+
+ name = die.GetName();
+ if (!name)
+ return nullptr;
+
+ DWARFDIE parent = die.GetParent();
+ if (!parent.IsStructUnionOrClass())
+ return nullptr;
+
+ const char *parent_name = parent.GetName();
+ if (!parent_name)
+ return nullptr;
+
+ // Constructor.
+ if (::strcmp(parent_name, name) == 0)
+ return name;
+
+ // Destructor.
+ if (name[0] == '~' && ::strcmp(parent_name, name + 1))
+ return name;
+
+ return nullptr;
+}
+
static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) {
- char const *name = die.GetMangledName(/*substitute_name_allowed*/ false);
+ char const *name = GetMangledOrStructorName(die);
if (!name)
return {};
+ auto *cu = die.GetCU();
+ if (!cu)
+ return {};
+
+ // FIXME: When resolving function call labels, we check that
+ // that the definition's DW_AT_specification points to the
+ // declaration that we encoded into the label here. But if the
+ // declaration came from a type-unit (and the definition from
+ // .debug_info), that check won't work. So for now, don't use
+ // function call labels for declaration DIEs from type-units.
+ if (cu->IsTypeUnit())
+ return {};
+
SymbolFileDWARF *dwarf = die.GetDWARF();
if (!dwarf)
return {};
@@ -285,7 +326,9 @@ static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) {
if (die_id == LLDB_INVALID_UID)
return {};
- return FunctionCallLabel{/*module_id=*/module_id,
+ // Note, discriminator is added by Clang during mangling.
+ return FunctionCallLabel{/*discriminator=*/{},
+ /*module_id=*/module_id,
/*symbol_id=*/die_id,
/*.lookup_name=*/name}
.toString();
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 42a66ce75d6d6..911ce5930ca4c 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -7,7 +7,9 @@
//===----------------------------------------------------------------------===//
#include "SymbolFileDWARF.h"
+#include "clang/Basic/ABI.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/Support/Casting.h"
@@ -78,6 +80,7 @@
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"
+#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatVariadic.h"
@@ -2482,6 +2485,133 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die,
return false;
}
+static int ClangToItaniumCtorKind(clang::CXXCtorType kind) {
+ switch (kind) {
+ case clang::CXXCtorType::Ctor_Complete:
+ return 1;
+ case clang::CXXCtorType::Ctor_Base:
+ return 2;
+ case clang::CXXCtorType::Ctor_CopyingClosure:
+ case clang::CXXCtorType::Ctor_DefaultClosure:
+ case clang::CXXCtorType::Ctor_Comdat:
+ llvm_unreachable("Unexpected constructor kind.");
+ }
+}
+
+static int ClangToItaniumDtorKind(clang::CXXDtorType kind) {
+ switch (kind) {
+ case clang::CXXDtorType::Dtor_Deleting:
+ return 0;
+ case clang::CXXDtorType::Dtor_Complete:
+ return 1;
+ case clang::CXXDtorType::Dtor_Base:
+ return 2;
+ case clang::CXXDtorType::Dtor_Comdat:
+ llvm_unreachable("Unexpected destructor kind.");
+ }
+}
+
+static std::optional<int>
+GetItaniumCtorDtorVariant(llvm::StringRef discriminator) {
+ const bool is_ctor = discriminator.consume_front("C");
+ if (!is_ctor && !discriminator.consume_front("D"))
+ return std::nullopt;
+
+ uint64_t structor_kind;
+ if (!llvm::to_integer(discriminator, structor_kind))
+ return std::nullopt;
+
+ if (is_ctor) {
+ if (structor_kind > clang::CXXCtorType::Ctor_DefaultClosure)
+ return std::nullopt;
+
+ return ClangToItaniumCtorKind(
+ static_cast<clang::CXXCtorType>(structor_kind));
+ }
+
+ if (structor_kind > clang::CXXDtorType::Dtor_Deleting)
+ return std::nullopt;
+
+ return ClangToItaniumDtorKind(static_cast<clang::CXXDtorType>(structor_kind));
+}
+
+DWARFDIE SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label,
+ const DWARFDIE &declaration) {
+ DWARFDIE definition;
+ llvm::DenseMap<int, DWARFDIE> structor_variant_to_die;
+
+ // eFunctionNameTypeFull for mangled name lookup.
+ // eFunctionNameTypeMethod is required for structor lookups (since we look
+ // those up by DW_AT_name).
+ Module::LookupInfo info(ConstString(label.lookup_name),
+ lldb::eFunctionNameTypeFull |
+ lldb::eFunctionNameTypeMethod,
+ lldb::eLanguageTypeUnknown);
+
+ m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
+ if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
+ return IterationAction::Continue;
+
+ auto spec = entry.GetAttributeValueAsReferenceDIE(DW_AT_specification);
+ if (!spec)
+ return IterationAction::Continue;
+
+ if (spec != declaration)
+ return IterationAction::Continue;
+
+ // We're not picking a specific structor variant DIE, so we're done here.
+ if (label.discriminator.empty()) {
+ definition = entry;
+ return IterationAction::Stop;
+ }
+
+ const char *mangled =
+ entry.GetMangledName(/*substitute_name_allowed=*/false);
+ if (!mangled)
+ return IterationAction::Continue;
+
+ llvm::ItaniumPartialDemangler D;
+ if (D.partialDemangle(mangled))
+ return IterationAction::Continue;
+
+ auto structor_variant = D.getCtorOrDtorVariant();
+ if (!structor_variant)
+ return IterationAction::Continue;
+
+ auto [_, inserted] = structor_variant_to_die.try_emplace(*structor_variant,
+ std::move(entry));
+ assert(inserted);
+
+ // The compiler may choose to alias the constructor variants
+ // (notably this happens on Linux), so we might not have a definition
+ // DIE for some structor variants. Hence we iterate over all variants
+ // and pick the most appropriate one out of those.
+ return IterationAction::Continue;
+ });
+
+ if (definition)
+ return definition;
+
+ auto label_variant = GetItaniumCtorDtorVariant(label.discriminator);
+ if (!label_variant)
+ return {};
+
+ auto it = structor_variant_to_die.find(*label_variant);
+
+ // Found the exact variant.
+ if (it != structor_variant_to_die.end())
+ return it->getSecond();
+
+ // C1 was aliased to C2
+ if (!label.lookup_name.starts_with("~") && label_variant == 1) {
+ if (auto it = structor_variant_to_die.find(2);
+ it != structor_variant_to_die.end())
+ return it->getSecond();
+ }
+
+ return {};
+}
+
llvm::Expected<SymbolContext>
SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
@@ -2494,24 +2624,7 @@ SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) {
// Label was created using a declaration DIE. Need to fetch the definition
// to resolve the function call.
if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) {
- Module::LookupInfo info(ConstString(label.lookup_name),
- lldb::eFunctionNameTypeFull,
- lldb::eLanguageTypeUnknown);
-
- m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
- if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
- return IterationAction::Continue;
-
- // We don't check whether the specification DIE for this function
- // corresponds to the declaration DIE because the declaration might be in
- // a type-unit but the definition in the compile-unit (and it's
- // specifcation would point to the declaration in the compile-unit). We
- // rely on the mangled name within the module to be enough to find us the
- // unique definition.
- die = entry;
- return IterationAction::Stop;
- });
-
+ die = FindFunctionDefinition(label, die);
if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
return llvm::createStringError(
llvm::formatv("failed to find definition DIE for {0}", label));
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index 3ec538da8cf77..843346619db37 100644
--- a/lldb/s...
[truncated]
|
@llvm/pr-subscribers-libcxxabi Author: Michael Buch (Michael137) ChangesDepends on #148877 Patch is 30.28 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/149827.diff 16 Files Affected:
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index b3ff45b3e90a3..a0efb21218312 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1059,22 +1059,13 @@ def AVRSignal : InheritableAttr, TargetSpecificAttr<TargetAVR> {
def AsmLabel : InheritableAttr {
let Spellings = [CustomKeyword<"asm">, CustomKeyword<"__asm__">];
let Args = [
- // Label specifies the mangled name for the decl.
- StringArgument<"Label">,
-
- // IsLiteralLabel specifies whether the label is literal (i.e. suppresses
- // the global C symbol prefix) or not. If not, the mangle-suppression prefix
- // ('\01') is omitted from the decl name at the LLVM IR level.
- //
- // Non-literal labels are used by some external AST sources like LLDB.
- BoolArgument<"IsLiteralLabel", /*optional=*/0, /*fake=*/1>
- ];
+ // Label specifies the mangled name for the decl.
+ StringArgument<"Label">, ];
let SemaHandler = 0;
let Documentation = [AsmLabelDocs];
- let AdditionalMembers =
-[{
+ let AdditionalMembers = [{
bool isEquivalent(AsmLabelAttr *Other) const {
- return getLabel() == Other->getLabel() && getIsLiteralLabel() == Other->getIsLiteralLabel();
+ return getLabel() == Other->getLabel();
}
}];
}
diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp
index 9652fdbc4e125..58029476572c5 100644
--- a/clang/lib/AST/Mangle.cpp
+++ b/clang/lib/AST/Mangle.cpp
@@ -152,6 +152,20 @@ bool MangleContext::shouldMangleDeclName(const NamedDecl *D) {
return shouldMangleCXXName(D);
}
+static llvm::StringRef g_lldb_func_call_label_prefix = "$__lldb_func";
+
+static void emitLLDBAsmLabel(llvm::StringRef label, GlobalDecl GD,
+ llvm::raw_ostream &Out) {
+ Out << g_lldb_func_call_label_prefix << ":";
+
+ if (llvm::isa<clang::CXXConstructorDecl>(GD.getDecl()))
+ Out << "C" << GD.getCtorType();
+ else if (llvm::isa<clang::CXXDestructorDecl>(GD.getDecl()))
+ Out << "D" << GD.getDtorType();
+
+ Out << label.substr(g_lldb_func_call_label_prefix.size() + 1);
+}
+
void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
const ASTContext &ASTContext = getASTContext();
const NamedDecl *D = cast<NamedDecl>(GD.getDecl());
@@ -161,9 +175,9 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
if (const AsmLabelAttr *ALA = D->getAttr<AsmLabelAttr>()) {
// If we have an asm name, then we use it as the mangling.
- // If the label isn't literal, or if this is an alias for an LLVM intrinsic,
+ // If the label is an alias for an LLVM intrinsic,
// do not add a "\01" prefix.
- if (!ALA->getIsLiteralLabel() || ALA->getLabel().starts_with("llvm.")) {
+ if (ALA->getLabel().starts_with("llvm.")) {
Out << ALA->getLabel();
return;
}
@@ -185,7 +199,11 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
if (!UserLabelPrefix.empty())
Out << '\01'; // LLVM IR Marker for __asm("foo")
- Out << ALA->getLabel();
+ if (ALA->getLabel().starts_with(g_lldb_func_call_label_prefix))
+ emitLLDBAsmLabel(ALA->getLabel(), GD, Out);
+ else
+ Out << ALA->getLabel();
+
return;
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index e2ac648320c0f..885d04b9ab926 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8113,9 +8113,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
}
}
- NewVD->addAttr(AsmLabelAttr::Create(Context, Label,
- /*IsLiteralLabel=*/true,
- SE->getStrTokenLoc(0)));
+ NewVD->addAttr(AsmLabelAttr::Create(Context, Label, SE->getStrTokenLoc(0)));
} else if (!ExtnameUndeclaredIdentifiers.empty()) {
llvm::DenseMap<IdentifierInfo*,AsmLabelAttr*>::iterator I =
ExtnameUndeclaredIdentifiers.find(NewVD->getIdentifier());
@@ -10345,9 +10343,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
if (Expr *E = D.getAsmLabel()) {
// The parser guarantees this is a string.
StringLiteral *SE = cast<StringLiteral>(E);
- NewFD->addAttr(AsmLabelAttr::Create(Context, SE->getString(),
- /*IsLiteralLabel=*/true,
- SE->getStrTokenLoc(0)));
+ NewFD->addAttr(
+ AsmLabelAttr::Create(Context, SE->getString(), SE->getStrTokenLoc(0)));
} else if (!ExtnameUndeclaredIdentifiers.empty()) {
llvm::DenseMap<IdentifierInfo*,AsmLabelAttr*>::iterator I =
ExtnameUndeclaredIdentifiers.find(NewFD->getIdentifier());
@@ -20598,8 +20595,8 @@ void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name,
LookupOrdinaryName);
AttributeCommonInfo Info(AliasName, SourceRange(AliasNameLoc),
AttributeCommonInfo::Form::Pragma());
- AsmLabelAttr *Attr = AsmLabelAttr::CreateImplicit(
- Context, AliasName->getName(), /*IsLiteralLabel=*/true, Info);
+ AsmLabelAttr *Attr =
+ AsmLabelAttr::CreateImplicit(Context, AliasName->getName(), Info);
// If a declaration that:
// 1) declares a function or a variable
diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp
index ed635da683aab..afaf413493299 100644
--- a/clang/unittests/AST/DeclTest.cpp
+++ b/clang/unittests/AST/DeclTest.cpp
@@ -74,7 +74,6 @@ TEST(Decl, AsmLabelAttr) {
StringRef Code = R"(
struct S {
void f() {}
- void g() {}
};
)";
auto AST =
@@ -87,11 +86,8 @@ TEST(Decl, AsmLabelAttr) {
const auto *DeclS =
selectFirst<CXXRecordDecl>("d", match(cxxRecordDecl().bind("d"), Ctx));
NamedDecl *DeclF = *DeclS->method_begin();
- NamedDecl *DeclG = *(++DeclS->method_begin());
- // Attach asm labels to the decls: one literal, and one not.
- DeclF->addAttr(AsmLabelAttr::Create(Ctx, "foo", /*LiteralLabel=*/true));
- DeclG->addAttr(AsmLabelAttr::Create(Ctx, "goo", /*LiteralLabel=*/false));
+ DeclF->addAttr(AsmLabelAttr::Create(Ctx, "foo"));
// Mangle the decl names.
std::string MangleF, MangleG;
@@ -99,14 +95,11 @@ TEST(Decl, AsmLabelAttr) {
ItaniumMangleContext::create(Ctx, Diags));
{
llvm::raw_string_ostream OS_F(MangleF);
- llvm::raw_string_ostream OS_G(MangleG);
MC->mangleName(DeclF, OS_F);
- MC->mangleName(DeclG, OS_G);
}
ASSERT_EQ(MangleF, "\x01"
"foo");
- ASSERT_EQ(MangleG, "goo");
}
TEST(Decl, MangleDependentSizedArray) {
diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h
index 6f27da7b9cadf..7b3983bc89367 100644
--- a/libcxxabi/src/demangle/ItaniumDemangle.h
+++ b/libcxxabi/src/demangle/ItaniumDemangle.h
@@ -1766,6 +1766,8 @@ class CtorDtorName final : public Node {
template<typename Fn> void match(Fn F) const { F(Basename, IsDtor, Variant); }
+ int getVariant() const { return Variant; }
+
void printLeft(OutputBuffer &OB) const override {
if (IsDtor)
OB += "~";
diff --git a/lldb/include/lldb/Expression/Expression.h b/lldb/include/lldb/Expression/Expression.h
index 20067f469895b..847226167d584 100644
--- a/lldb/include/lldb/Expression/Expression.h
+++ b/lldb/include/lldb/Expression/Expression.h
@@ -103,11 +103,15 @@ class Expression {
///
/// The format being:
///
-/// <prefix>:<module uid>:<symbol uid>:<name>
+/// <prefix>:<discriminator>:<module uid>:<symbol uid>:<name>
///
/// The label string needs to stay valid for the entire lifetime
/// of this object.
struct FunctionCallLabel {
+ /// Arbitrary string which language plugins can interpret for their
+ /// own needs.
+ llvm::StringRef discriminator;
+
/// Unique identifier of the lldb_private::Module
/// which contains the symbol identified by \c symbol_id.
lldb::user_id_t module_id;
@@ -133,7 +137,7 @@ struct FunctionCallLabel {
///
/// The representation roundtrips through \c fromString:
/// \code{.cpp}
- /// llvm::StringRef encoded = "$__lldb_func:0x0:0x0:_Z3foov";
+ /// llvm::StringRef encoded = "$__lldb_func:blah:0x0:0x0:_Z3foov";
/// FunctionCallLabel label = *fromString(label);
///
/// assert (label.toString() == encoded);
diff --git a/lldb/source/Expression/Expression.cpp b/lldb/source/Expression/Expression.cpp
index 796851ff15ca3..16ecb1d7deef8 100644
--- a/lldb/source/Expression/Expression.cpp
+++ b/lldb/source/Expression/Expression.cpp
@@ -34,10 +34,10 @@ Expression::Expression(ExecutionContextScope &exe_scope)
llvm::Expected<FunctionCallLabel>
lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
- llvm::SmallVector<llvm::StringRef, 4> components;
- label.split(components, ":", /*MaxSplit=*/3);
+ llvm::SmallVector<llvm::StringRef, 5> components;
+ label.split(components, ":", /*MaxSplit=*/4);
- if (components.size() != 4)
+ if (components.size() != 5)
return llvm::createStringError("malformed function call label.");
if (components[0] != FunctionCallLabelPrefix)
@@ -45,8 +45,10 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
"expected function call label prefix '{0}' but found '{1}' instead.",
FunctionCallLabelPrefix, components[0]));
- llvm::StringRef module_label = components[1];
- llvm::StringRef die_label = components[2];
+ llvm::StringRef discriminator = components[1];
+ llvm::StringRef module_label = components[2];
+ llvm::StringRef die_label = components[3];
+ llvm::StringRef lookup_name = components[4];
lldb::user_id_t module_id = 0;
if (!llvm::to_integer(module_label, module_id))
@@ -58,20 +60,23 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
return llvm::createStringError(
llvm::formatv("failed to parse symbol ID from '{0}'.", die_label));
- return FunctionCallLabel{/*.module_id=*/module_id,
+ return FunctionCallLabel{/*.discriminator=*/discriminator,
+ /*.module_id=*/module_id,
/*.symbol_id=*/die_id,
- /*.lookup_name=*/components[3]};
+ /*.lookup_name=*/lookup_name};
}
std::string lldb_private::FunctionCallLabel::toString() const {
- return llvm::formatv("{0}:{1:x}:{2:x}:{3}", FunctionCallLabelPrefix,
- module_id, symbol_id, lookup_name)
+ return llvm::formatv("{0}:{1}:{2:x}:{3:x}:{4}", FunctionCallLabelPrefix,
+ discriminator, module_id, symbol_id, lookup_name)
.str();
}
void llvm::format_provider<FunctionCallLabel>::format(
const FunctionCallLabel &label, raw_ostream &OS, StringRef Style) {
- OS << llvm::formatv("FunctionCallLabel{ module_id: {0:x}, symbol_id: {1:x}, "
- "lookup_name: {2} }",
- label.module_id, label.symbol_id, label.lookup_name);
+ OS << llvm::formatv("FunctionCallLabel{ discriminator: {0}, module_id: "
+ "{1:x}, symbol_id: {2:x}, "
+ "lookup_name: {3} }",
+ label.discriminator, label.module_id, label.symbol_id,
+ label.lookup_name);
}
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index 781c1c6c5745d..5c674308160e5 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -250,11 +250,52 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram,
return cv_quals;
}
+static const char *GetMangledOrStructorName(const DWARFDIE &die) {
+ const char *name = die.GetMangledName(/*substitute_name_allowed*/ false);
+ if (name)
+ return name;
+
+ name = die.GetName();
+ if (!name)
+ return nullptr;
+
+ DWARFDIE parent = die.GetParent();
+ if (!parent.IsStructUnionOrClass())
+ return nullptr;
+
+ const char *parent_name = parent.GetName();
+ if (!parent_name)
+ return nullptr;
+
+ // Constructor.
+ if (::strcmp(parent_name, name) == 0)
+ return name;
+
+ // Destructor.
+ if (name[0] == '~' && ::strcmp(parent_name, name + 1))
+ return name;
+
+ return nullptr;
+}
+
static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) {
- char const *name = die.GetMangledName(/*substitute_name_allowed*/ false);
+ char const *name = GetMangledOrStructorName(die);
if (!name)
return {};
+ auto *cu = die.GetCU();
+ if (!cu)
+ return {};
+
+ // FIXME: When resolving function call labels, we check that
+ // that the definition's DW_AT_specification points to the
+ // declaration that we encoded into the label here. But if the
+ // declaration came from a type-unit (and the definition from
+ // .debug_info), that check won't work. So for now, don't use
+ // function call labels for declaration DIEs from type-units.
+ if (cu->IsTypeUnit())
+ return {};
+
SymbolFileDWARF *dwarf = die.GetDWARF();
if (!dwarf)
return {};
@@ -285,7 +326,9 @@ static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) {
if (die_id == LLDB_INVALID_UID)
return {};
- return FunctionCallLabel{/*module_id=*/module_id,
+ // Note, discriminator is added by Clang during mangling.
+ return FunctionCallLabel{/*discriminator=*/{},
+ /*module_id=*/module_id,
/*symbol_id=*/die_id,
/*.lookup_name=*/name}
.toString();
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 42a66ce75d6d6..911ce5930ca4c 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -7,7 +7,9 @@
//===----------------------------------------------------------------------===//
#include "SymbolFileDWARF.h"
+#include "clang/Basic/ABI.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/Support/Casting.h"
@@ -78,6 +80,7 @@
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"
+#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatVariadic.h"
@@ -2482,6 +2485,133 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die,
return false;
}
+static int ClangToItaniumCtorKind(clang::CXXCtorType kind) {
+ switch (kind) {
+ case clang::CXXCtorType::Ctor_Complete:
+ return 1;
+ case clang::CXXCtorType::Ctor_Base:
+ return 2;
+ case clang::CXXCtorType::Ctor_CopyingClosure:
+ case clang::CXXCtorType::Ctor_DefaultClosure:
+ case clang::CXXCtorType::Ctor_Comdat:
+ llvm_unreachable("Unexpected constructor kind.");
+ }
+}
+
+static int ClangToItaniumDtorKind(clang::CXXDtorType kind) {
+ switch (kind) {
+ case clang::CXXDtorType::Dtor_Deleting:
+ return 0;
+ case clang::CXXDtorType::Dtor_Complete:
+ return 1;
+ case clang::CXXDtorType::Dtor_Base:
+ return 2;
+ case clang::CXXDtorType::Dtor_Comdat:
+ llvm_unreachable("Unexpected destructor kind.");
+ }
+}
+
+static std::optional<int>
+GetItaniumCtorDtorVariant(llvm::StringRef discriminator) {
+ const bool is_ctor = discriminator.consume_front("C");
+ if (!is_ctor && !discriminator.consume_front("D"))
+ return std::nullopt;
+
+ uint64_t structor_kind;
+ if (!llvm::to_integer(discriminator, structor_kind))
+ return std::nullopt;
+
+ if (is_ctor) {
+ if (structor_kind > clang::CXXCtorType::Ctor_DefaultClosure)
+ return std::nullopt;
+
+ return ClangToItaniumCtorKind(
+ static_cast<clang::CXXCtorType>(structor_kind));
+ }
+
+ if (structor_kind > clang::CXXDtorType::Dtor_Deleting)
+ return std::nullopt;
+
+ return ClangToItaniumDtorKind(static_cast<clang::CXXDtorType>(structor_kind));
+}
+
+DWARFDIE SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label,
+ const DWARFDIE &declaration) {
+ DWARFDIE definition;
+ llvm::DenseMap<int, DWARFDIE> structor_variant_to_die;
+
+ // eFunctionNameTypeFull for mangled name lookup.
+ // eFunctionNameTypeMethod is required for structor lookups (since we look
+ // those up by DW_AT_name).
+ Module::LookupInfo info(ConstString(label.lookup_name),
+ lldb::eFunctionNameTypeFull |
+ lldb::eFunctionNameTypeMethod,
+ lldb::eLanguageTypeUnknown);
+
+ m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
+ if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
+ return IterationAction::Continue;
+
+ auto spec = entry.GetAttributeValueAsReferenceDIE(DW_AT_specification);
+ if (!spec)
+ return IterationAction::Continue;
+
+ if (spec != declaration)
+ return IterationAction::Continue;
+
+ // We're not picking a specific structor variant DIE, so we're done here.
+ if (label.discriminator.empty()) {
+ definition = entry;
+ return IterationAction::Stop;
+ }
+
+ const char *mangled =
+ entry.GetMangledName(/*substitute_name_allowed=*/false);
+ if (!mangled)
+ return IterationAction::Continue;
+
+ llvm::ItaniumPartialDemangler D;
+ if (D.partialDemangle(mangled))
+ return IterationAction::Continue;
+
+ auto structor_variant = D.getCtorOrDtorVariant();
+ if (!structor_variant)
+ return IterationAction::Continue;
+
+ auto [_, inserted] = structor_variant_to_die.try_emplace(*structor_variant,
+ std::move(entry));
+ assert(inserted);
+
+ // The compiler may choose to alias the constructor variants
+ // (notably this happens on Linux), so we might not have a definition
+ // DIE for some structor variants. Hence we iterate over all variants
+ // and pick the most appropriate one out of those.
+ return IterationAction::Continue;
+ });
+
+ if (definition)
+ return definition;
+
+ auto label_variant = GetItaniumCtorDtorVariant(label.discriminator);
+ if (!label_variant)
+ return {};
+
+ auto it = structor_variant_to_die.find(*label_variant);
+
+ // Found the exact variant.
+ if (it != structor_variant_to_die.end())
+ return it->getSecond();
+
+ // C1 was aliased to C2
+ if (!label.lookup_name.starts_with("~") && label_variant == 1) {
+ if (auto it = structor_variant_to_die.find(2);
+ it != structor_variant_to_die.end())
+ return it->getSecond();
+ }
+
+ return {};
+}
+
llvm::Expected<SymbolContext>
SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
@@ -2494,24 +2624,7 @@ SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) {
// Label was created using a declaration DIE. Need to fetch the definition
// to resolve the function call.
if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) {
- Module::LookupInfo info(ConstString(label.lookup_name),
- lldb::eFunctionNameTypeFull,
- lldb::eLanguageTypeUnknown);
-
- m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
- if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
- return IterationAction::Continue;
-
- // We don't check whether the specification DIE for this function
- // corresponds to the declaration DIE because the declaration might be in
- // a type-unit but the definition in the compile-unit (and it's
- // specifcation would point to the declaration in the compile-unit). We
- // rely on the mangled name within the module to be enough to find us the
- // unique definition.
- die = entry;
- return IterationAction::Stop;
- });
-
+ die = FindFunctionDefinition(label, die);
if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
return llvm::createStringError(
llvm::formatv("failed to find definition DIE for {0}", label));
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index 3ec538da8cf77..843346619db37 100644
--- a/lldb/s...
[truncated]
|
@llvm/pr-subscribers-clang Author: Michael Buch (Michael137) ChangesDepends on #148877 Patch is 30.28 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/149827.diff 16 Files Affected:
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index b3ff45b3e90a3..a0efb21218312 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1059,22 +1059,13 @@ def AVRSignal : InheritableAttr, TargetSpecificAttr<TargetAVR> {
def AsmLabel : InheritableAttr {
let Spellings = [CustomKeyword<"asm">, CustomKeyword<"__asm__">];
let Args = [
- // Label specifies the mangled name for the decl.
- StringArgument<"Label">,
-
- // IsLiteralLabel specifies whether the label is literal (i.e. suppresses
- // the global C symbol prefix) or not. If not, the mangle-suppression prefix
- // ('\01') is omitted from the decl name at the LLVM IR level.
- //
- // Non-literal labels are used by some external AST sources like LLDB.
- BoolArgument<"IsLiteralLabel", /*optional=*/0, /*fake=*/1>
- ];
+ // Label specifies the mangled name for the decl.
+ StringArgument<"Label">, ];
let SemaHandler = 0;
let Documentation = [AsmLabelDocs];
- let AdditionalMembers =
-[{
+ let AdditionalMembers = [{
bool isEquivalent(AsmLabelAttr *Other) const {
- return getLabel() == Other->getLabel() && getIsLiteralLabel() == Other->getIsLiteralLabel();
+ return getLabel() == Other->getLabel();
}
}];
}
diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp
index 9652fdbc4e125..58029476572c5 100644
--- a/clang/lib/AST/Mangle.cpp
+++ b/clang/lib/AST/Mangle.cpp
@@ -152,6 +152,20 @@ bool MangleContext::shouldMangleDeclName(const NamedDecl *D) {
return shouldMangleCXXName(D);
}
+static llvm::StringRef g_lldb_func_call_label_prefix = "$__lldb_func";
+
+static void emitLLDBAsmLabel(llvm::StringRef label, GlobalDecl GD,
+ llvm::raw_ostream &Out) {
+ Out << g_lldb_func_call_label_prefix << ":";
+
+ if (llvm::isa<clang::CXXConstructorDecl>(GD.getDecl()))
+ Out << "C" << GD.getCtorType();
+ else if (llvm::isa<clang::CXXDestructorDecl>(GD.getDecl()))
+ Out << "D" << GD.getDtorType();
+
+ Out << label.substr(g_lldb_func_call_label_prefix.size() + 1);
+}
+
void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
const ASTContext &ASTContext = getASTContext();
const NamedDecl *D = cast<NamedDecl>(GD.getDecl());
@@ -161,9 +175,9 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
if (const AsmLabelAttr *ALA = D->getAttr<AsmLabelAttr>()) {
// If we have an asm name, then we use it as the mangling.
- // If the label isn't literal, or if this is an alias for an LLVM intrinsic,
+ // If the label is an alias for an LLVM intrinsic,
// do not add a "\01" prefix.
- if (!ALA->getIsLiteralLabel() || ALA->getLabel().starts_with("llvm.")) {
+ if (ALA->getLabel().starts_with("llvm.")) {
Out << ALA->getLabel();
return;
}
@@ -185,7 +199,11 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
if (!UserLabelPrefix.empty())
Out << '\01'; // LLVM IR Marker for __asm("foo")
- Out << ALA->getLabel();
+ if (ALA->getLabel().starts_with(g_lldb_func_call_label_prefix))
+ emitLLDBAsmLabel(ALA->getLabel(), GD, Out);
+ else
+ Out << ALA->getLabel();
+
return;
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index e2ac648320c0f..885d04b9ab926 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8113,9 +8113,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
}
}
- NewVD->addAttr(AsmLabelAttr::Create(Context, Label,
- /*IsLiteralLabel=*/true,
- SE->getStrTokenLoc(0)));
+ NewVD->addAttr(AsmLabelAttr::Create(Context, Label, SE->getStrTokenLoc(0)));
} else if (!ExtnameUndeclaredIdentifiers.empty()) {
llvm::DenseMap<IdentifierInfo*,AsmLabelAttr*>::iterator I =
ExtnameUndeclaredIdentifiers.find(NewVD->getIdentifier());
@@ -10345,9 +10343,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
if (Expr *E = D.getAsmLabel()) {
// The parser guarantees this is a string.
StringLiteral *SE = cast<StringLiteral>(E);
- NewFD->addAttr(AsmLabelAttr::Create(Context, SE->getString(),
- /*IsLiteralLabel=*/true,
- SE->getStrTokenLoc(0)));
+ NewFD->addAttr(
+ AsmLabelAttr::Create(Context, SE->getString(), SE->getStrTokenLoc(0)));
} else if (!ExtnameUndeclaredIdentifiers.empty()) {
llvm::DenseMap<IdentifierInfo*,AsmLabelAttr*>::iterator I =
ExtnameUndeclaredIdentifiers.find(NewFD->getIdentifier());
@@ -20598,8 +20595,8 @@ void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name,
LookupOrdinaryName);
AttributeCommonInfo Info(AliasName, SourceRange(AliasNameLoc),
AttributeCommonInfo::Form::Pragma());
- AsmLabelAttr *Attr = AsmLabelAttr::CreateImplicit(
- Context, AliasName->getName(), /*IsLiteralLabel=*/true, Info);
+ AsmLabelAttr *Attr =
+ AsmLabelAttr::CreateImplicit(Context, AliasName->getName(), Info);
// If a declaration that:
// 1) declares a function or a variable
diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp
index ed635da683aab..afaf413493299 100644
--- a/clang/unittests/AST/DeclTest.cpp
+++ b/clang/unittests/AST/DeclTest.cpp
@@ -74,7 +74,6 @@ TEST(Decl, AsmLabelAttr) {
StringRef Code = R"(
struct S {
void f() {}
- void g() {}
};
)";
auto AST =
@@ -87,11 +86,8 @@ TEST(Decl, AsmLabelAttr) {
const auto *DeclS =
selectFirst<CXXRecordDecl>("d", match(cxxRecordDecl().bind("d"), Ctx));
NamedDecl *DeclF = *DeclS->method_begin();
- NamedDecl *DeclG = *(++DeclS->method_begin());
- // Attach asm labels to the decls: one literal, and one not.
- DeclF->addAttr(AsmLabelAttr::Create(Ctx, "foo", /*LiteralLabel=*/true));
- DeclG->addAttr(AsmLabelAttr::Create(Ctx, "goo", /*LiteralLabel=*/false));
+ DeclF->addAttr(AsmLabelAttr::Create(Ctx, "foo"));
// Mangle the decl names.
std::string MangleF, MangleG;
@@ -99,14 +95,11 @@ TEST(Decl, AsmLabelAttr) {
ItaniumMangleContext::create(Ctx, Diags));
{
llvm::raw_string_ostream OS_F(MangleF);
- llvm::raw_string_ostream OS_G(MangleG);
MC->mangleName(DeclF, OS_F);
- MC->mangleName(DeclG, OS_G);
}
ASSERT_EQ(MangleF, "\x01"
"foo");
- ASSERT_EQ(MangleG, "goo");
}
TEST(Decl, MangleDependentSizedArray) {
diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h
index 6f27da7b9cadf..7b3983bc89367 100644
--- a/libcxxabi/src/demangle/ItaniumDemangle.h
+++ b/libcxxabi/src/demangle/ItaniumDemangle.h
@@ -1766,6 +1766,8 @@ class CtorDtorName final : public Node {
template<typename Fn> void match(Fn F) const { F(Basename, IsDtor, Variant); }
+ int getVariant() const { return Variant; }
+
void printLeft(OutputBuffer &OB) const override {
if (IsDtor)
OB += "~";
diff --git a/lldb/include/lldb/Expression/Expression.h b/lldb/include/lldb/Expression/Expression.h
index 20067f469895b..847226167d584 100644
--- a/lldb/include/lldb/Expression/Expression.h
+++ b/lldb/include/lldb/Expression/Expression.h
@@ -103,11 +103,15 @@ class Expression {
///
/// The format being:
///
-/// <prefix>:<module uid>:<symbol uid>:<name>
+/// <prefix>:<discriminator>:<module uid>:<symbol uid>:<name>
///
/// The label string needs to stay valid for the entire lifetime
/// of this object.
struct FunctionCallLabel {
+ /// Arbitrary string which language plugins can interpret for their
+ /// own needs.
+ llvm::StringRef discriminator;
+
/// Unique identifier of the lldb_private::Module
/// which contains the symbol identified by \c symbol_id.
lldb::user_id_t module_id;
@@ -133,7 +137,7 @@ struct FunctionCallLabel {
///
/// The representation roundtrips through \c fromString:
/// \code{.cpp}
- /// llvm::StringRef encoded = "$__lldb_func:0x0:0x0:_Z3foov";
+ /// llvm::StringRef encoded = "$__lldb_func:blah:0x0:0x0:_Z3foov";
/// FunctionCallLabel label = *fromString(label);
///
/// assert (label.toString() == encoded);
diff --git a/lldb/source/Expression/Expression.cpp b/lldb/source/Expression/Expression.cpp
index 796851ff15ca3..16ecb1d7deef8 100644
--- a/lldb/source/Expression/Expression.cpp
+++ b/lldb/source/Expression/Expression.cpp
@@ -34,10 +34,10 @@ Expression::Expression(ExecutionContextScope &exe_scope)
llvm::Expected<FunctionCallLabel>
lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
- llvm::SmallVector<llvm::StringRef, 4> components;
- label.split(components, ":", /*MaxSplit=*/3);
+ llvm::SmallVector<llvm::StringRef, 5> components;
+ label.split(components, ":", /*MaxSplit=*/4);
- if (components.size() != 4)
+ if (components.size() != 5)
return llvm::createStringError("malformed function call label.");
if (components[0] != FunctionCallLabelPrefix)
@@ -45,8 +45,10 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
"expected function call label prefix '{0}' but found '{1}' instead.",
FunctionCallLabelPrefix, components[0]));
- llvm::StringRef module_label = components[1];
- llvm::StringRef die_label = components[2];
+ llvm::StringRef discriminator = components[1];
+ llvm::StringRef module_label = components[2];
+ llvm::StringRef die_label = components[3];
+ llvm::StringRef lookup_name = components[4];
lldb::user_id_t module_id = 0;
if (!llvm::to_integer(module_label, module_id))
@@ -58,20 +60,23 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
return llvm::createStringError(
llvm::formatv("failed to parse symbol ID from '{0}'.", die_label));
- return FunctionCallLabel{/*.module_id=*/module_id,
+ return FunctionCallLabel{/*.discriminator=*/discriminator,
+ /*.module_id=*/module_id,
/*.symbol_id=*/die_id,
- /*.lookup_name=*/components[3]};
+ /*.lookup_name=*/lookup_name};
}
std::string lldb_private::FunctionCallLabel::toString() const {
- return llvm::formatv("{0}:{1:x}:{2:x}:{3}", FunctionCallLabelPrefix,
- module_id, symbol_id, lookup_name)
+ return llvm::formatv("{0}:{1}:{2:x}:{3:x}:{4}", FunctionCallLabelPrefix,
+ discriminator, module_id, symbol_id, lookup_name)
.str();
}
void llvm::format_provider<FunctionCallLabel>::format(
const FunctionCallLabel &label, raw_ostream &OS, StringRef Style) {
- OS << llvm::formatv("FunctionCallLabel{ module_id: {0:x}, symbol_id: {1:x}, "
- "lookup_name: {2} }",
- label.module_id, label.symbol_id, label.lookup_name);
+ OS << llvm::formatv("FunctionCallLabel{ discriminator: {0}, module_id: "
+ "{1:x}, symbol_id: {2:x}, "
+ "lookup_name: {3} }",
+ label.discriminator, label.module_id, label.symbol_id,
+ label.lookup_name);
}
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index 781c1c6c5745d..5c674308160e5 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -250,11 +250,52 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram,
return cv_quals;
}
+static const char *GetMangledOrStructorName(const DWARFDIE &die) {
+ const char *name = die.GetMangledName(/*substitute_name_allowed*/ false);
+ if (name)
+ return name;
+
+ name = die.GetName();
+ if (!name)
+ return nullptr;
+
+ DWARFDIE parent = die.GetParent();
+ if (!parent.IsStructUnionOrClass())
+ return nullptr;
+
+ const char *parent_name = parent.GetName();
+ if (!parent_name)
+ return nullptr;
+
+ // Constructor.
+ if (::strcmp(parent_name, name) == 0)
+ return name;
+
+ // Destructor.
+ if (name[0] == '~' && ::strcmp(parent_name, name + 1))
+ return name;
+
+ return nullptr;
+}
+
static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) {
- char const *name = die.GetMangledName(/*substitute_name_allowed*/ false);
+ char const *name = GetMangledOrStructorName(die);
if (!name)
return {};
+ auto *cu = die.GetCU();
+ if (!cu)
+ return {};
+
+ // FIXME: When resolving function call labels, we check that
+ // that the definition's DW_AT_specification points to the
+ // declaration that we encoded into the label here. But if the
+ // declaration came from a type-unit (and the definition from
+ // .debug_info), that check won't work. So for now, don't use
+ // function call labels for declaration DIEs from type-units.
+ if (cu->IsTypeUnit())
+ return {};
+
SymbolFileDWARF *dwarf = die.GetDWARF();
if (!dwarf)
return {};
@@ -285,7 +326,9 @@ static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) {
if (die_id == LLDB_INVALID_UID)
return {};
- return FunctionCallLabel{/*module_id=*/module_id,
+ // Note, discriminator is added by Clang during mangling.
+ return FunctionCallLabel{/*discriminator=*/{},
+ /*module_id=*/module_id,
/*symbol_id=*/die_id,
/*.lookup_name=*/name}
.toString();
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 42a66ce75d6d6..911ce5930ca4c 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -7,7 +7,9 @@
//===----------------------------------------------------------------------===//
#include "SymbolFileDWARF.h"
+#include "clang/Basic/ABI.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/Support/Casting.h"
@@ -78,6 +80,7 @@
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"
+#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatVariadic.h"
@@ -2482,6 +2485,133 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die,
return false;
}
+static int ClangToItaniumCtorKind(clang::CXXCtorType kind) {
+ switch (kind) {
+ case clang::CXXCtorType::Ctor_Complete:
+ return 1;
+ case clang::CXXCtorType::Ctor_Base:
+ return 2;
+ case clang::CXXCtorType::Ctor_CopyingClosure:
+ case clang::CXXCtorType::Ctor_DefaultClosure:
+ case clang::CXXCtorType::Ctor_Comdat:
+ llvm_unreachable("Unexpected constructor kind.");
+ }
+}
+
+static int ClangToItaniumDtorKind(clang::CXXDtorType kind) {
+ switch (kind) {
+ case clang::CXXDtorType::Dtor_Deleting:
+ return 0;
+ case clang::CXXDtorType::Dtor_Complete:
+ return 1;
+ case clang::CXXDtorType::Dtor_Base:
+ return 2;
+ case clang::CXXDtorType::Dtor_Comdat:
+ llvm_unreachable("Unexpected destructor kind.");
+ }
+}
+
+static std::optional<int>
+GetItaniumCtorDtorVariant(llvm::StringRef discriminator) {
+ const bool is_ctor = discriminator.consume_front("C");
+ if (!is_ctor && !discriminator.consume_front("D"))
+ return std::nullopt;
+
+ uint64_t structor_kind;
+ if (!llvm::to_integer(discriminator, structor_kind))
+ return std::nullopt;
+
+ if (is_ctor) {
+ if (structor_kind > clang::CXXCtorType::Ctor_DefaultClosure)
+ return std::nullopt;
+
+ return ClangToItaniumCtorKind(
+ static_cast<clang::CXXCtorType>(structor_kind));
+ }
+
+ if (structor_kind > clang::CXXDtorType::Dtor_Deleting)
+ return std::nullopt;
+
+ return ClangToItaniumDtorKind(static_cast<clang::CXXDtorType>(structor_kind));
+}
+
+DWARFDIE SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label,
+ const DWARFDIE &declaration) {
+ DWARFDIE definition;
+ llvm::DenseMap<int, DWARFDIE> structor_variant_to_die;
+
+ // eFunctionNameTypeFull for mangled name lookup.
+ // eFunctionNameTypeMethod is required for structor lookups (since we look
+ // those up by DW_AT_name).
+ Module::LookupInfo info(ConstString(label.lookup_name),
+ lldb::eFunctionNameTypeFull |
+ lldb::eFunctionNameTypeMethod,
+ lldb::eLanguageTypeUnknown);
+
+ m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
+ if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
+ return IterationAction::Continue;
+
+ auto spec = entry.GetAttributeValueAsReferenceDIE(DW_AT_specification);
+ if (!spec)
+ return IterationAction::Continue;
+
+ if (spec != declaration)
+ return IterationAction::Continue;
+
+ // We're not picking a specific structor variant DIE, so we're done here.
+ if (label.discriminator.empty()) {
+ definition = entry;
+ return IterationAction::Stop;
+ }
+
+ const char *mangled =
+ entry.GetMangledName(/*substitute_name_allowed=*/false);
+ if (!mangled)
+ return IterationAction::Continue;
+
+ llvm::ItaniumPartialDemangler D;
+ if (D.partialDemangle(mangled))
+ return IterationAction::Continue;
+
+ auto structor_variant = D.getCtorOrDtorVariant();
+ if (!structor_variant)
+ return IterationAction::Continue;
+
+ auto [_, inserted] = structor_variant_to_die.try_emplace(*structor_variant,
+ std::move(entry));
+ assert(inserted);
+
+ // The compiler may choose to alias the constructor variants
+ // (notably this happens on Linux), so we might not have a definition
+ // DIE for some structor variants. Hence we iterate over all variants
+ // and pick the most appropriate one out of those.
+ return IterationAction::Continue;
+ });
+
+ if (definition)
+ return definition;
+
+ auto label_variant = GetItaniumCtorDtorVariant(label.discriminator);
+ if (!label_variant)
+ return {};
+
+ auto it = structor_variant_to_die.find(*label_variant);
+
+ // Found the exact variant.
+ if (it != structor_variant_to_die.end())
+ return it->getSecond();
+
+ // C1 was aliased to C2
+ if (!label.lookup_name.starts_with("~") && label_variant == 1) {
+ if (auto it = structor_variant_to_die.find(2);
+ it != structor_variant_to_die.end())
+ return it->getSecond();
+ }
+
+ return {};
+}
+
llvm::Expected<SymbolContext>
SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
@@ -2494,24 +2624,7 @@ SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) {
// Label was created using a declaration DIE. Need to fetch the definition
// to resolve the function call.
if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) {
- Module::LookupInfo info(ConstString(label.lookup_name),
- lldb::eFunctionNameTypeFull,
- lldb::eLanguageTypeUnknown);
-
- m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
- if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
- return IterationAction::Continue;
-
- // We don't check whether the specification DIE for this function
- // corresponds to the declaration DIE because the declaration might be in
- // a type-unit but the definition in the compile-unit (and it's
- // specifcation would point to the declaration in the compile-unit). We
- // rely on the mangled name within the module to be enough to find us the
- // unique definition.
- die = entry;
- return IterationAction::Stop;
- });
-
+ die = FindFunctionDefinition(label, die);
if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
return llvm::createStringError(
llvm::formatv("failed to find definition DIE for {0}", label));
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index 3ec538da8cf77..843346619db37 100644
--- a/lldb/s...
[truncated]
|
…for LLDB JIT expressions This patch adds special handling for `AsmLabel`s created by LLDB. LLDB uses `AsmLabel`s to encode information about a function declaration to make it easier to locate function symbols when JITing C++ expressions. For constructors/destructors LLDB doesn't know at the time of creating the `AsmLabelAttr` which structor variant the expression evaluator will need to call (this is decided when compiling the expression). So we make the Clang mangler inject this information into our custom label when we're JITting the expression.
This patch adds a way to retrieve the constructor/destructor variant from the Itanium demangle tree. This will be used by LLDB.
This patch is an implementation of [this discussion](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816/7) about handling ABI-tagged structors during expression evaluation. **Motivation** LLDB encodes the mangled name of a `DW_TAG_subprogram` into `AsmLabel`s on function and method Clang AST nodes. This means that when calls to these functions get lowered into IR (when running JITted expressions), the address resolver can locate the appropriate symbol by mangled name (and it's guaranteed to find the symbol because we got the mangled name from debug-info, instead of letting Clang mangle it based on AST structure). However, we don't do this for `CXXConstructorDecl`s/`CXXDestructorDecl`s because these structor declarations in DWARF don't have a linkage name. This is because there can be multiple variants of a structor, each with a distinct mangling in the Itanium ABI. Each structor variant has its own definition `DW_TAG_subprogram`. So LLDB doesn't know which mangled name to put into the `AsmLabel`. Currently this means using an ABI-tagged constructor in LLDB expressions won't work (see [this RFC](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816) for concrete examples). **Proposed Solution** The `FunctionCallLabel` encoding that we put into `AsmLabel`s already supports stuffing more info about a DIE into it. So this patch extends the `FunctionCallLabel` to contain an optional discriminator (a sequence of bytes) which the `SymbolFileDWARF` plugin interprets as the constructor/destructor variant of that DIE. There's a few subtleties here: 1. At the point at which LLDB first constructs the label, it has no way of knowing (just by looking the debug-info declaration), which structor variant the expression evaluator is supposed to call. That's something that gets decided when compiling the expression. So we let the Clang mangler inject the correct structor variant into the `AsmLabel` during JITing. I adjusted the `AsmLabelAttr` mangling for this. An option would've been to create a new Clang attribute which behaved like an `AsmLabel` but with these special semantics for LLDB. My main concern there is that we'd have to adjust all the `AsmLabelAttr` checks around Clang to also now account for this new attribute. 2. The compiler is free to omit the `C1` variant of a constructor if the `C2` variant is sufficient. In that case it may alias `C1` to `C2`, leaving us with only the `C2` `DW_TAG_subprogram` in the object file. Linux is one of the platforms where this occurs. For those cases there's heuristic in `SymbolFileDWARF` where we pick `C2` if we asked for `C1` but it doesn't exist. This may not always be correct (if for some reason the compiler decided to drop `C1` for other reasons).
9852fd8
to
52da84e
Compare
You can test this locally with the following command:darker --check --diff -r HEAD~1...HEAD lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py lldb/test/API/lang/cpp/constructors/TestCppConstructors.py lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py View the diff from darker here.--- constructors/TestCppConstructors.py 2025-08-14 11:54:08.000000 +0000
+++ constructors/TestCppConstructors.py 2025-08-14 12:22:35.438829 +0000
@@ -37,20 +37,20 @@
error=True,
substrs=["Couldn't look up symbols:"],
)
# FIXME: Calling deleted constructors should fail before linking.
- #self.expect(
+ # self.expect(
# "expr ClassWithDeletedCtor(1).value",
# error=True,
# substrs=["Couldn't look up symbols:"],
- #)
- #self.expect(
+ # )
+ # self.expect(
# "expr ClassWithDeletedDefaultCtor().value",
# error=True,
# substrs=["Couldn't look up symbols:", "function", "optimized out"],
- #)
+ # )
@skipIfWindows # Can't find operator new.
@skipIfLinux # Fails on some Linux systems with SIGABRT.
def test_constructors_new(self):
self.build()
|
52da84e
to
f8725a9
Compare
@labath Thanks for the reviews so far. The latest commit contains a prototype of adding the GCC-style unified mangled name to constructor/destructor declarations. Turns out this wasn't too hard and actually solves all the issues we talked about pretty nicely. Mainly needed to ensure the accelerator tables map the unified mangled name to the definition DIE. And thus also had to adjust the The issue of checking that the specification came from the right place is redundant now because we lookup structors within a SymbolFile up by mangled name too. And the fallback path in case the definition is in a different module works too because we encode the mangled structor name in the label (albeit the unified one, so there may still need to be some work to be done to ensure we pick the right variant). Also for older clang versions, if the structor declaration doesn't have a mangled name, which just don't attach an Let me know what you think. I'll prepare a separate PR to discuss these changes |
f8725a9
to
6fc22b7
Compare
…llvm#148877) LLDB currently attaches `AsmLabel`s to `FunctionDecl`s such that that the `IRExecutionUnit` can determine which mangled name to call (we can't rely on Clang deriving the correct mangled name to call because the debug-info AST doesn't contain all the info that would be encoded in the DWARF linkage names). However, we don't attach `AsmLabel`s for structors because they have multiple variants and thus it's not clear which mangled name to use. In the [RFC on fixing expression evaluation of abi-tagged structors](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816) we discussed encoding the structor variant into the `AsmLabel`s. Specifically in [this thread](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816/7) we discussed that the contents of the `AsmLabel` are completely under LLDB's control and we could make use of it to uniquely identify a function by encoding the exact module and DIE that the function is associated with (mangled names need not be enough since two identical mangled symbols may live in different modules). So if we already have a custom `AsmLabel` format, we can encode the structor variant in a follow-up (the current idea is to append the structor variant as a suffix to our custom `AsmLabel` when Clang emits the mangled name into the JITted IR). Then we would just have to teach the `IRExecutionUnit` to pick the correct structor variant DIE during symbol resolution. The draft of this is available [here](llvm#149827) This patch sets up the infrastructure for the custom `AsmLabel` format by encoding the module id, DIE id and mangled name in it. **Implementation** The flow is as follows: 1. Create the label in `DWARFASTParserClang`. The format is: `$__lldb_func:module_id:die_id:mangled_name` 2. When resolving external symbols in `IRExecutionUnit`, we parse this label and then do a lookup by DIE ID (or mangled name into the module if the encoded DIE is a declaration). Depends on llvm#151355
This was added purely for the needs of LLDB. However, starting with llvm#151355 and llvm#148877 we no longer create literal AsmLabels in LLDB either. So this is unused and we can get rid of it. In the near future LLDB will want to add special support for mangling `AsmLabel`s (specifically llvm#149827).
FYI, measured the impact of adding a unified
|
a 15% growth to object files is pretty rough - but I guess this is without compression enabled, which'll /probably/ save us (Google)... @cmtice / @labath you folks might want to check what the impact of this change (@Michael137 do you have a PR to point to that folks could test with) |
Here's a draft PR: #153369 It's the bare minimum to get my prototype working |
I ran our internal debug info metrics tests, and while the numbers appear to be better than what you reported, the increase is still of the magnitude that would cause a problem for us. The increase is about 3-4% of binary size. The bulk of the increase comes from debug_str and debug_str_offsets (both around 7-8%). Our largest binary doesn't even build as it goes over the 4GB debug_str limit. debug_info also increases a bit (<1%). Funnily enough, debug_abbrev actually decreases, presumably because the abbreviations can be shared with regular method declarations, but that doesn't matter as abbreviations are small anyway. I'm rerunning the numbers to confirm this (I originally tested with the version in this PR and not #153369), but I don't expect this to make a difference. |
Thanks for checking this!
Am I misremembering that this came up before elsewhere? I guess that's what the simple-template-name stuff fixed? Not sure where we go from here then if this is a blocker. Maybe there's a way to reduce the amount of linkage names we emit, though not sure off the top how we would decide that. The only reason I tried doing it the GCC way was that we could look up the structor definitions without having to do structural matching on the DIE context. I suppose we can go back to that method. That would also still leave the cross-module case an open question.. |
Allows us to use the mangling substitution facilities in CPlusPlusLanguage but also SymbolFileDWARF.
…r during lookup Now that we emit a unified linkage name to structor declarations we can use it to perform lookups. Before performing the lookup we substitute the discriminator into the linkage name, and we also try substituting for potential structor aliases. This solves the case where declarations and definitions of structors live in separate SymbolFile's because the fallback global lookup that the IRExecutionUnit does will use the substituted mangled name to do so.
Latest commit has the changes that make use of the unified structor mangling to do the cross-module lookup. Still not the prettiest because it relies on the And from digging around, it looks like on linux we already rely on the |
I re-ran the number, and the results are roughly the same.
Yes, simple template names came about because some targets were overflowing the debug_str section. I'm kinda surprised it's overflowing again, but I don't know the details/numbers there.
It's definitely a "problem", but I'm not sure if it's a "blocker". We had some discussions about what we could do to absorb that cost, but it's not trivial and it's getting kind of over my head. The thing is that, while the size increase is unfortunate, the change also makes a lot sense as it makes things consistent, both with gcc and with clang's non-structor functions. This week is also pretty busy for all the llvm people, so we don't really have an official position on this.
I don't know either. I was looking at the generated debug info with @dwblaikie, and we didn't find anything obvious.
I like the structural match method, as it would open the door to potentially removing the linkage names from declaration DIEs (thereby saving debug_str space), but that's a more speculative approach, with unknown performance and other possible issues, so I don't feel entirely comfortable asking you to go down that path. The cross module thing is tricky, but I don't think it's fundamentally unsolvable -- basically, we just need to find a way to pass DIE information from one module to another.
Yeah, that's because linux (elf?) will simply not generate the symbol for the C1 if the ABI doesn't require it to do that (and it's equivalent to C2). This actually brings back (painful) memories of when I (some 10 years ago) tried to remove this "optimization" and it caused an large increase in the .symtab section. |
Depends on #148877
This patch is an implementation of this discussion about handling ABI-tagged structors during expression evaluation.
Motivation
LLDB encodes the mangled name of a
DW_TAG_subprogram
intoAsmLabel
s on function and method Clang AST nodes. This means that when calls to these functions get lowered into IR (when running JITted expressions), the address resolver can locate the appropriate symbol by mangled name (and it is guaranteed to find the symbol because we got the mangled name from debug-info, instead of letting Clang mangle it based on AST structure). However, we don't do this forCXXConstructorDecl
s/CXXDestructorDecl
s because these structor declarations in DWARF don't have a linkage name. This is because there can be multiple variants of a structor, each with a distinct mangling in the Itanium ABI. Each structor variant has its own definitionDW_TAG_subprogram
. So LLDB doesn't know which mangled name to put into theAsmLabel
.Currently this means using ABI-tagged structors in LLDB expressions won't work (see this RFC for concrete examples).
Proposed Solution
The
FunctionCallLabel
encoding that we put intoAsmLabel
s already supports stuffing more info about a DIE into it. So this patch extends theFunctionCallLabel
to contain an optional discriminator (a sequence of bytes) which theSymbolFileDWARF
plugin interprets as the constructor/destructor variant of that DIE. So when searching for the definition DIE, LLDB will include the structor variant in its heuristic for determining a match.There's a few subtleties here:
AsmLabel
during JITing. I adjusted theAsmLabelAttr
mangling for this. An option would've been to create a new Clang attribute which behaved like anAsmLabel
but with these special semantics for LLDB. My main concern there is that we'd have to adjust all theAsmLabelAttr
checks around Clang to also now account for this new attribute.C1
variant of a constructor if theC2
variant is sufficient. In that case it may aliasC1
toC2
, leaving us with only theC2
DW_TAG_subprogram
in the object file. Linux is one of the platforms where this occurs. For those cases I added a heuristic inSymbolFileDWARF
where we pickC2
if we asked forC1
but it doesn't exist. This may not always be correct (e.g., if the compiler decided to dropC1
for other reasons).